/*
 * FLV.cpp
 *
 *  Created on: Mar 28, 2013
 *      Author: root
 */

#include "FLV.h"

/*amf data info*/
static const AMFObjectProperty AMFProp_Invalid = { {NULL, 0}, AMF_INVALID };
static const AVal AV_empty = { NULL , 0 };
static const char *  av_onMetaData = "onMetaData";
static const char *  av_width = "width";
static const char *  av_height = "height";
static const char *  av_videodatarate = "videodatarate";
static const char *  av_framerate = "framerate";
static const char *  av_audiosamplerate = "audiosamplerate";
static const char *  av_audiosamplesize = "audiosamplesize";
static const char *  av_stereo = "stereo";
static const char *  av_audiodatarate = "audiodatarate";
static const char *  av_default_attribute = "DEFAULT_ATTRIBUTE";


/**
 * class init
 */
FLV::FLV() {
	// TODO Auto-generated constructor stub

}

/**
 * class free
 */
FLV::~FLV() {
	// TODO Auto-generated destructor stub
}

/*
 * out log ,please overwrite function
 * */
int	FLV::log_fmt(int nloglvl,const char * lpszFormat, ...)
{
	va_list argList;
	char m_szMsg[8192];
	memset(m_szMsg,'\0',8192);

	va_start(argList, lpszFormat);
	vsprintf(m_szMsg, lpszFormat, argList);
	va_end(argList);

	printf("log :%s\r\n",m_szMsg);

	return SUCCESS;
}

/*
*amf prop set/get
*/
void FLV::AMFProp_GetName(AMFObjectProperty * prop, AVal * name)
{
	*name = prop->p_name;
}

void FLV::AMFProp_SetName(AMFObjectProperty * prop, AVal * name)
{
	prop->p_name = *name;
}

AMFDataType FLV::AMFProp_GetType(AMFObjectProperty * prop)
{
	return prop->p_type;
}

double FLV::AMFProp_GetNumber(AMFObjectProperty * prop)
{
	return prop->p_vu.p_number;
}

int FLV::AMFProp_GetBoolean(AMFObjectProperty * prop)
{
	return prop->p_vu.p_number != 0;
}

void FLV::AMFProp_GetString(AMFObjectProperty * prop, AVal * str)
{
	*str = prop->p_vu.p_aval;
}

void FLV::AMFProp_GetObject(AMFObjectProperty * prop, AMFObject * obj)
{
	*obj = prop->p_vu.p_object;
}

int FLV::AMFProp_IsValid(AMFObjectProperty * prop)
{
	return prop->p_type != AMF_INVALID;
}

int FLV::AMF_CountProp(AMFObject * obj)
{
	return obj->o_num;
}

void FLV::AMF_AddProp(AMFObject * obj, const AMFObjectProperty * prop)
{
	if (!(obj->o_num & 0x0f))
	{
		obj->o_props = (AMFObjectProperty *)realloc(obj->o_props, (obj->o_num + 16) * sizeof(AMFObjectProperty));
	}
	obj->o_props[obj->o_num++] = *prop;
}
AMFObjectProperty * FLV::AMF_GetProp(AMFObject * obj, const AVal * name, int nIndex)
{
	if (nIndex >= 0)
	{
		if (nIndex <= obj->o_num)
			return &obj->o_props[nIndex];
	}
	else
	{
		int n;
		for (n = 0; n < obj->o_num; n++)
		{
			if (AVMATCH(&obj->o_props[n].p_name, name))
				return &obj->o_props[n];
		}
	}
	return (AMFObjectProperty *) & AMFProp_Invalid;
}
void FLV::AMFProp_Dump(AMFObjectProperty * prop)
{
	char strRes[1024] = "";
	char str[4096] = "";
	AVal name;

	if (prop->p_type == AMF_INVALID)
	{
		log_fmt(LOG_ALL, "AMF Property: INVALID");
		return;
	}

	if (prop->p_type == AMF_NULL)
	{
		log_fmt(LOG_ALL, "AMF Property: NULL");
		return;
	}

	if (prop->p_name.av_len)
	{
		name = prop->p_name;
		if (name.av_len > 25)
			name.av_len = 25;

		snprintf(strRes, 255, "Name: %25.*s, ", name.av_len, name.av_val);
	}

	if (prop->p_type == AMF_OBJECT)
	{
		log_fmt(LOG_ALL, "Property: <%sOBJECT>", strRes);
		AMF_Dump(&prop->p_vu.p_object);
		return;
	}
	switch (prop->p_type)
	{
		case AMF_NUMBER:
			snprintf(str, 255, "NUMBER:\t%.2f", prop->p_vu.p_number);
		break;
		case AMF_BOOLEAN:
			snprintf(str, 255, "BOOLEAN:\t%s",
			prop->p_vu.p_number != 0.0 ? "TRUE" : "FALSE");
			break;
		case AMF_STRING:
			snprintf(str, 255, "STRING:\t%.*s", prop->p_vu.p_aval.av_len,
			prop->p_vu.p_aval.av_val);
			break;
		case AMF_DATE:
			snprintf(str, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d",
			prop->p_vu.p_number, prop->p_UTCoffset);
			break;
		default:
			snprintf(str, 255, "INVALID TYPE 0x%02x", (char) prop->p_type);
			break;
	}

	log_fmt(LOG_ALL,  "Property: <%s%s>", strRes, str);

	printf("Property: <%s%s>\r\n", strRes, str);
}

void FLV::AMFProp_Reset(AMFObjectProperty * prop)
{
	if (prop->p_type == AMF_OBJECT)
		AMF_Reset(&prop->p_vu.p_object);
	else
	{
		prop->p_vu.p_aval.av_len = 0;
		prop->p_vu.p_aval.av_val = NULL;
	}
	prop->p_type = AMF_INVALID;
}

void FLV::AMF_Dump(AMFObject * obj)
{
	int n;
	log_fmt(LOG_ALL, "(object begin)");
	for (n = 0; n < obj->o_num; n++)
	{
		AMFProp_Dump(&obj->o_props[n]);
	}
	log_fmt(LOG_ALL, "(object end)");
}

void FLV::AMF_Reset(AMFObject * obj)
{
	int n;
	for (n = 0; n < obj->o_num; n++)
	{
		AMFProp_Reset(&obj->o_props[n]);
	}
	if(obj->o_props != NULL)
	{
		free(obj->o_props);
	}
	obj->o_props = NULL;
	obj->o_num = 0;
}
/*
*amf data decode
*/
uint16_t FLV::AMF_DecodeInt8(const char *data)
{
	return data[0];
}

uint16_t FLV::AMF_DecodeInt16(const char *data)
{
    return (data[0] << 8) |
         (data[1] << 0);
}
uint32_t FLV::AMF_DecodeInt24(const char *data)
{
   return (data[0] << 16) |
          (data[1] << 8)  |
          (data[2] << 0);
}
uint32_t FLV::AMF_DecodeInt32(const char *data)
{
  return (data[0] << 24) |
         (data[1] << 16) |
         (data[2] << 8)  |
         (data[3] << 0);
}
uint64_t FLV::AMF_DecodeInt64(const char *data)
{
	return ((uint64_t)(AMF_DecodeInt32(data)) << 32) + AMF_DecodeInt32(data + 4);
}
double FLV::AMF_DecodeNumber(const char *data)
{
	#if __FLOAT_WORD_ORDER == __BYTE_ORDER
		#if __BYTE_ORDER == __BIG_ENDIAN
			double dVal;
			memcpy(&dVal, data, 8);
			return dVal;
		#elif __BYTE_ORDER == __LITTLE_ENDIAN
			double dVal;
			unsigned char *ci, *co;
			ci = (unsigned char *) data;
			co = (unsigned char *) &dVal;
			co[0] = ci[7];
			co[1] = ci[6];
			co[2] = ci[5];
			co[3] = ci[4];
			co[4] = ci[3];
			co[5] = ci[2];
			co[6] = ci[1];
			co[7] = ci[0];
			return dVal;
		#endif
	#else
		#if __BYTE_ORDER == __LITTLE_ENDIAN	// __FLOAT_WORD_ORER == __BIG_ENDIAN
			uint32_t in1 = *((uint32_t *) data);
			uint32_t in2 = *((uint32_t *) (data + 4));

			in1 = __bswap_32(in1);
			in2 = __bswap_32(in2);

			uint64_t res = ((uint64_t) in2 << 32) | (uint64_t) in1;
			return *((double *) &res);
		#else // __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN
			uint32_t in1 = *((uint32_t *) data);
			uint32_t in2 = *((uint32_t *) (data + 4));

			uint64_t res = ((uint64_t) in1 << 32) | (uint64_t) in2;
			return *((double *) &res);
		#endif
	#endif
}
void FLV::AMF_DecodeString(const char *data, AVal * bv)
{
	bv->av_len = AMF_DecodeInt16(data);
	bv->av_val = (bv->av_len > 0) ? (char *) data + 2 : NULL;
}
bool FLV::AMF_DecodeBoolean(const char *data)
{
	return *data != 0;
}
int FLV::AMF3ReadInteger(const char *data, int32_t * valp)
{
	int i = 0;
	int32_t val = 0;
	while (i <= 2)
	{
		/* handle first 3 bytes */
		if (data[i] & 0x80)
		{
			// byte used
			val <<= 7;		// shift up
			val |= (data[i] & 0x7f);	// add bits
			i++;
		}
		else
		{
			break;
		}
	}
	if (i > 2)
	{
		// use 4th byte, all 8bits
		val <<= 8;
		val |= data[3];

		// range check
		if (val > AMF3_INTEGER_MAX)
		val -= (1 << 29);
	}
	else
	{
		// use 7bits of last unparsed byte (0xxxxxxx)
		val <<= 7;
		val |= data[i];
	}
	*valp = val;
	return i > 2 ? 4 : i + 1;
}
int FLV::AMF3ReadString(const char *data, AVal * str)
{
	int32_t ref = 0;
	int len = AMF3ReadInteger(data, &ref);
	data += len;
	if ((ref & 0x1) == 0)
	{
		/* reference: 0xxx */
		/*uint32_t refIndex = (ref >> 1);*/
		return len;
	}
	else
	{
		uint32_t nSize = (ref >> 1);
		str->av_val = (char *) data;
		str->av_len = nSize;
		return len + nSize;
	}
	return len;
}
int FLV::AMF3_Decode(AMFObject * obj, const char *pBuffer, int nSize, bool bAMFData)
{
	int nOriginalSize = nSize;
	int32_t ref;
	int len;

	obj->o_num = 0;
	obj->o_props = NULL;
	if (bAMFData)
	{
		if (*pBuffer != AMF3_OBJECT)
			log_fmt(LOG_ERR, "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!");

		pBuffer++;
		nSize--;
	}
	ref = 0;
	len = AMF3ReadInteger(pBuffer, &ref);
	pBuffer += len;
	nSize -= len;

	if ((ref & 1) == 0)
	{
		/* object reference, 0xxx */
		uint32_t objectIndex = (ref >> 1);
		log_fmt(LOG_ALL, "Object reference, index: %d", objectIndex);
	}
	else/* object instance */
	{
		int32_t classRef = (ref >> 1);
		AMF3ClassDef cd = { {0, 0}};
		AMFObjectProperty prop;
		if ((classRef & 0x1) == 0)
		{
			/* class reference */
			uint32_t classIndex = (classRef >> 1);
			log_fmt(LOG_ALL, "Class reference: %d", classIndex);
		}
		else
		{
			int32_t classExtRef = (classRef >> 1);
			int i;
			cd.cd_externalizable = (classExtRef & 0x1) == 1;
			cd.cd_dynamic = ((classExtRef >> 1) & 0x1) == 1;
			cd.cd_num = classExtRef >> 2;
			// class name
			len = AMF3ReadString(pBuffer, &cd.cd_name);
			nSize -= len;
			pBuffer += len;
			log_fmt(LOG_ALL,"Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d",
				cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic,cd.cd_num);

			for (i = 0; i < cd.cd_num; i++)
			{
				AVal memberName;
				memberName.av_len = 0;
				memberName.av_val = NULL;
				len = AMF3ReadString(pBuffer, &memberName);
				log_fmt(LOG_ALL, "Member: %s", memberName.av_val);
				AMF3CD_AddProp(&cd, &memberName);
				nSize -= len;
				pBuffer += len;
			}
		}
		/* add as referencable object */
		if (cd.cd_externalizable)
		{
			int nRes;
			AVal name;
			name.av_val = (char *)av_default_attribute;
			name.av_len = strlen(av_default_attribute);
			log_fmt(LOG_ALL, "Externalizable, TODO check");

			nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, false);
			if (nRes == -1)
			{
				log_fmt(LOG_ALL, "%s, failed to decode AMF3 property!",__FUNCTION__);
			}
			else
			{
				nSize -= nRes;
				pBuffer += nRes;
			}
			AMFProp_SetName(&prop, &name);
			AMF_AddProp(obj, &prop);
		}
		else
		{
			int nRes, i;
			for (i = 0; i < cd.cd_num; i++)	/* non-dynamic */
			{
				nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, false);
				if (nRes == -1)
					log_fmt(LOG_ALL, "%s, failed to decode AMF3 property!",__FUNCTION__);

				AMFProp_SetName(&prop, AMF3CD_GetProp(&cd, i));
				AMF_AddProp(obj, &prop);

				pBuffer += nRes;
				nSize -= nRes;
			}
			if (cd.cd_dynamic)
			{
				int len = 0;
				do
				{
					nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, true);
					AMF_AddProp(obj, &prop);

					pBuffer += nRes;
					nSize -= nRes;

					len = prop.p_name.av_len;
				}
				while (len > 0);
			}
		}
		log_fmt(LOG_ALL, "class object!");
	}
	return nOriginalSize - nSize;
}
int FLV::AMF3Prop_Decode(AMFObjectProperty * prop, const char *data, int nSize,int bDecodeName)
{
	int nOriginalSize = nSize;
	AMF3DataType type;
	prop->p_name.av_len = 0;
	prop->p_name.av_val = NULL;
	if (nSize == 0 || !data)
	{
		log_fmt(LOG_ALL, "empty buffer/no buffer pointer!");
		return -1;
	}
	/* decode name */
	if (bDecodeName)
    {
      AVal name;
      name.av_len = 0;
      name.av_val = NULL;

      int nRes = AMF3ReadString(data, &name);
      if (name.av_len <= 0)
		return nRes;
      prop->p_name = name;
      data += nRes;
      nSize -= nRes;
    }

	/* decode */
	type =AMF3DataType (*data++);
	nSize--;

	switch (type)
	{
		case AMF3_UNDEFINED:
		case AMF3_NULL:
			prop->p_type = AMF_NULL;
			break;
		case AMF3_FALSE:
			prop->p_type = AMF_BOOLEAN;
			prop->p_vu.p_number = 0.0;
			break;
		case AMF3_TRUE:
			prop->p_type = AMF_BOOLEAN;
			prop->p_vu.p_number = 1.0;
			break;
		case AMF3_INTEGER:
			{
				int32_t res = 0;
				int len = AMF3ReadInteger(data, &res);
				prop->p_vu.p_number = (double) res;
				prop->p_type = AMF_NUMBER;
				nSize -= len;
				break;
			}
		case AMF3_DOUBLE:
			if (nSize < 8)
				return -1;
			prop->p_vu.p_number = AMF_DecodeNumber(data);
			prop->p_type = AMF_NUMBER;
			nSize -= 8;
			break;
		case AMF3_STRING:
		case AMF3_XML_DOC:
		case AMF3_XML:
			{
				int len = AMF3ReadString(data, &prop->p_vu.p_aval);
				prop->p_type = AMF_STRING;
				nSize -= len;
				break;
			}
		case AMF3_DATE:
			{
				int32_t res = 0;
				int len = AMF3ReadInteger(data, &res);

				nSize -= len;
				data += len;

				if ((res & 0x1) == 0)
				{			/* reference */
					uint32_t nIndex = (res >> 1);
					log_fmt(LOG_ALL, "AMF3_DATE reference: %d, not supported!", nIndex);
				}
				else
				{
					if (nSize < 8)
						return -1;
					prop->p_vu.p_number = AMF_DecodeNumber(data);
					nSize -= 8;
					prop->p_type = AMF_NUMBER;
				}
				break;
			}
		case AMF3_OBJECT:
			{
				int nRes = AMF3_Decode(&prop->p_vu.p_object, data, nSize, true);
				if (nRes == -1)
				return -1;
				nSize -= nRes;
				prop->p_type = AMF_OBJECT;
				break;
			}
		case AMF3_ARRAY:
		case AMF3_BYTE_ARRAY:
		default:
			log_fmt(LOG_ALL, "%s - AMF3 unknown/unsupported datatype", __FUNCTION__);
			return -1;
	}
	return nOriginalSize - nSize;
}
int FLV::AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize,int nArrayLen, bool bDecodeName)
{
	int nOriginalSize = nSize;
	bool bError = false;
	obj->o_num = 0;
	obj->o_props = NULL;
	while (nArrayLen > 0)
	{
		nArrayLen--;

		AMFObjectProperty prop;
		int nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
		if (nRes == -1)
			bError = true;
		else
		{
			nSize -= nRes;
			pBuffer += nRes;
			AMF_AddProp(obj, &prop);
		}
	}
	if (bError)
		return -1;
	return nOriginalSize - nSize;
}
int FLV::AMFProp_Decode(AMFObjectProperty * prop, const char *pBuffer, int nSize,int bDecodeName)
{
	int nOriginalSize = nSize;
	prop->p_name.av_len = 0;
	prop->p_name.av_val = NULL;
	if (nSize == 0 || !pBuffer)
	{
		log_fmt(LOG_ALL,  "%s: Empty buffer/no buffer pointer!", __FUNCTION__);
		return -1;
	}
	if (bDecodeName && nSize < 4)
	{
		/* at least name (length + at least 1 byte) and 1 byte of data */
		log_fmt(LOG_ALL,	  "%s: Not enough data for decoding with name, less then 4 bytes!",	  __FUNCTION__);
		return -1;
	}
	if (bDecodeName)
	{
		unsigned short nNameSize = AMF_DecodeInt16(pBuffer);
		if (nNameSize > nSize - 2)
		{
			log_fmt(LOG_ALL, "%s: Name size out of range: namesize (%d) > len (%d) - 2", __FUNCTION__, nNameSize, nSize);
			return -1;
		}
		AMF_DecodeString(pBuffer, &prop->p_name);
		nSize -= 2 + nNameSize;
		pBuffer += 2 + nNameSize;
	}
	if (nSize == 0)
	{
		return -1;
	}
	nSize--;
	prop->p_type = (AMFDataType)(*pBuffer++);
	switch (prop->p_type)
    {
	case AMF_NUMBER:
		if (nSize < 8)
			return -1;
		prop->p_vu.p_number =  AMF_DecodeNumber(pBuffer);
		nSize -= 8;
		break;
	case AMF_BOOLEAN:
		if (nSize < 1)
			return -1;
		prop->p_vu.p_number = (double) AMF_DecodeBoolean(pBuffer);
		nSize--;
		break;
	case AMF_STRING:
		{
			unsigned short nStringSize = AMF_DecodeInt16(pBuffer);

			if (nSize < (long) nStringSize + 2)
				return -1;
			AMF_DecodeString(pBuffer, &prop->p_vu.p_aval);
			nSize -= (2 + nStringSize);
			break;
		}
	case AMF_OBJECT:
		{
			int nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer, nSize, true);
			if (nRes == -1)
				return -1;
			nSize -= nRes;
			break;
		}
	case AMF_MOVIECLIP:
		{
			log_fmt(LOG_ERR, "AMF_MOVIECLIP reserved!");
			return -1;
			break;
		}
	case AMF_NULL:
	case AMF_UNDEFINED:
	case AMF_UNSUPPORTED:
		prop->p_type = AMF_NULL;
		break;
	case AMF_REFERENCE:
		{
			log_fmt(LOG_ERR, "AMF_REFERENCE not supported!");
			return -1;
			break;
		}
	case AMF_ECMA_ARRAY:
		{
			nSize -= 4;
			/* next comes the rest, mixed array has a final 0x000009 mark and names, so its an object */
			int nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer + 4, nSize, true);
			if (nRes == -1)
				return -1;
			nSize -= nRes;
			prop->p_type = AMF_OBJECT;
			break;
		}
	case AMF_OBJECT_END:
		{
			return -1;
			break;
		}
	case AMF_STRICT_ARRAY:
		{
			unsigned int nArrayLen = AMF_DecodeInt32(pBuffer);
			nSize -= 4;
			int nRes = AMF_DecodeArray(&prop->p_vu.p_object, pBuffer + 4, nSize,nArrayLen, false);
			if (nRes == -1)
				return -1;
			nSize -= nRes;
			prop->p_type = AMF_OBJECT;
			break;
		}
	case AMF_DATE:
		{
			log_fmt(LOG_ALL, "AMF_DATE");
			if (nSize < 10)
				return -1;

			prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
			prop->p_UTCoffset = AMF_DecodeInt16(pBuffer + 8);

			nSize -= 10;
			break;
		}
	case AMF_LONG_STRING:
		{
			unsigned int nStringSize = AMF_DecodeInt32(pBuffer);
			if (nSize < (long) nStringSize + 4)
				return -1;
			AMF_DecodeString(pBuffer, &prop->p_vu.p_aval);
			nSize -= (4 + nStringSize);
			prop->p_type = AMF_STRING;
			break;
		}
	case AMF_RECORDSET:
		{
			log_fmt(LOG_ERR, "AMF_RECORDSET reserved!");
			return -1;
			break;
		}
	case AMF_XML_DOC:
		{
			log_fmt(LOG_ERR, "AMF_XML_DOC not supported!");
			return -1;
			break;
		}
	case AMF_TYPED_OBJECT:
		{
			log_fmt(LOG_ERR, "AMF_TYPED_OBJECT not supported!");
			return -1;
			break;
		}
	case AMF_AVMPLUS:
		{
			int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, true);
			if (nRes == -1)
				return -1;
			nSize -= nRes;
			prop->p_type = AMF_OBJECT;
			break;
		}
	default:
		log_fmt(LOG_ALL, "%s - unknown datatype 0x%02x, @0x%08X", __FUNCTION__,prop->p_type, pBuffer - 1);
		return -1;
    }

  return nOriginalSize - nSize;
}
int FLV::AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize, bool bDecodeName)
{
	int nOriginalSize = nSize;
	bool bError = false;		/* if there is an error while decoding - try to at least find the end mark AMF_OBJECT_END */

	obj->o_num = 0;
	obj->o_props = NULL;

	while (nSize > 0)
	{
		AMFObjectProperty prop;
		int nRes;

		if (nSize >=3 && AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END)
		{
			nSize -= 3;
			bError = false;
			break;
		}

		if (bError)
		{
			log_fmt(LOG_WARN, "DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!");
			nSize--;
			pBuffer++;
			continue;
		}

		nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
		if (nRes == -1)
			bError = true;
		else
		{
			nSize -= nRes;
			pBuffer += nRes;
			AMF_AddProp(obj, &prop);
		}
	}

  if (bError)
    return -1;

  return nOriginalSize - nSize;
}


/*
*amf data encode
*/
char * FLV::AMF_EncodeInt8(char *data,size_t &data_len, char v)
{
	if(data_len < 1)
		return NULL;
    data[0] = v;
	data_len -= 1;
	return data + 1;
}
char * FLV::AMF_EncodeInt16(char *data,size_t &data_len, unsigned int v)
{
	if(data_len < 2)
		return NULL;
    data[0] = (char)(v >> 8);
    data[1] = (char)(v >> 0);
	data_len -= 2;
    return data + 2;
}
char * FLV::AMF_EncodeInt24(char *data,size_t &data_len, unsigned int v)
{
	if(data_len < 3)
		return NULL;
	data[0] = (char)(v >> 16);
	data[1] = (char)(v >> 8);
	data[2] = (char)(v >> 0);
	data_len -= 3;
	return data + 3;
}
char * FLV::AMF_EncodeInt32(char *data,size_t &data_len, uint32_t v)
{
	if(data_len < 4)
		return NULL;
	data[0] = (char)(v >> 24);
	data[1] = (char)(v >> 16);
	data[2] = (char)(v >> 8);
	data[3] = (char)(v >> 0);
	data_len -= 4;
	return data + 4;
}
char * FLV::AMF_EncodeInt64(char *data,size_t &data_len, uint64_t v)
{
	if(data_len < 8)
		return NULL;
	AMF_EncodeInt32(data + 0,data_len, (uint32_t)(v >> 32));
	AMF_EncodeInt32(data + 4,data_len, (uint32_t)(v >> 0));
	return data + 8;
}

char * FLV::AMF_EncodeString(char *data,size_t &data_len, const AVal * bv)
{
	if ((bv->av_len < 65536 &&  1 + 2 + bv->av_len > data_len) || 1 + 4 + bv->av_len > data_len)
		return NULL;

	if (bv->av_len < 65536)
	{
		*data++ = AMF_STRING;
		data_len--;
		data = AMF_EncodeInt16(data,data_len,  bv->av_len);
	}
	else
	{
		*data++ = AMF_LONG_STRING;
		data_len--;
		data = AMF_EncodeInt32(data, data_len, bv->av_len);
	}
	memcpy(data, bv->av_val, bv->av_len);
	data += bv->av_len;
	data_len -=  bv->av_len;
	return data;
}
char * FLV::AMF_EncodeNumber(char *data,size_t &data_len, double dVal)
{
	 if (9 > data_len)
		return NULL;
	*data++ = AMF_NUMBER;	// type: Number
	#if __FLOAT_WORD_ORDER == __BYTE_ORDER
		#if __BYTE_ORDER == __BIG_ENDIAN
			memcpy(data, &dVal, 8);
		#elif __BYTE_ORDER == __LITTLE_ENDIAN
		{
			char *ci, *co;
			ci = (char *) &dVal;
			co = (char *) data;
			co[0] = ci[7];
			co[1] = ci[6];
			co[2] = ci[5];
			co[3] = ci[4];
			co[4] = ci[3];
			co[5] = ci[2];
			co[6] = ci[1];
			co[7] = ci[0];
		}
		#endif
	#else
		#if __BYTE_ORDER == __LITTLE_ENDIAN	/* __FLOAT_WORD_ORER == __BIG_ENDIAN */
		{
			char *ci, *co;
			ci = (char *) &dVal;
			co = (char *) data;
			co[0] = ci[3];
			co[1] = ci[2];
			co[2] = ci[1];
			co[3] = ci[0];
			co[4] = ci[7];
			co[5] = ci[6];
			co[6] = ci[5];
			co[7] = ci[4];
		}
		#else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
		{
			char *ci, *co;
			ci = (char *) &dVal;
			co = (char *) data;
			co[0] = ci[4];
			co[1] = ci[5];
			co[2] = ci[6];
			co[3] = ci[7];
			co[4] = ci[0];
			co[5] = ci[1];
			co[6] = ci[2];
			co[7] = ci[3];
		}
		#endif
	#endif
	data_len -= 8;
	return data+8;
}
char * FLV::AMF_EncodeBoolean(char *data,size_t &data_len, bool bVal)
{
   if (2 > data_len)
    return NULL;
  *data++ = AMF_BOOLEAN;
  *data++ = bVal ? 0x01 : 0x00;
  data_len -= 2;
  return data;
}

char * FLV::AMF_EncodeName(char *data,size_t &data_len, const AVal * strName)
{
	if(2 + strName->av_len > data_len)
		return NULL;
	data = AMF_EncodeInt16(data,data_len, strName->av_len);
	memcpy(data, strName->av_val, strName->av_len);
	data += strName->av_len;
	data_len -= 2+strName->av_len;
	return data;
}
char * FLV::AMF_EncodeNamedString(char *data,size_t &data_len, const AVal * strName, const AVal * strValue)
{
	data = AMF_EncodeName(data,data_len,strName);
	if(data != NULL)
		return AMF_EncodeString(data,data_len,strValue);
	else
		return NULL;
}
char * FLV::AMF_EncodeNamedNumber(char *data,size_t &data_len, const AVal * strName, double dVal)
{
	data = AMF_EncodeName(data,data_len,strName);
	if(data != NULL)
		return AMF_EncodeNumber(data,data_len, dVal);
	else
		return NULL;
}

char * FLV::AMF_EncodeNamedBoolean(char *data,size_t &data_len, const AVal * strName, bool bVal)
{
	data = AMF_EncodeName(data,data_len,strName);
	if(data != NULL)
		return AMF_EncodeBoolean(data,data_len, bVal);
	else
		return NULL;
}
char * FLV::AMFProp_Encode(AMFObjectProperty * prop,char *data,size_t &data_len)
{
	if (prop->p_type == AMF_INVALID || prop->p_type == AMF_NULL)
		return NULL;
	if (prop->p_type != AMF_NULL && prop->p_name.av_len + 2 + 1 >= data_len)
		return NULL;

	if (prop->p_type != AMF_NULL && prop->p_name.av_len)
	{
		*data++ = prop->p_name.av_len >> 8;
		data_len--;
		*data++ = prop->p_name.av_len & 0xff;
		data_len--;
		memcpy(data, prop->p_name.av_val, prop->p_name.av_len);
		data += prop->p_name.av_len;
		data_len -= prop->p_name.av_len;
	}
	switch (prop->p_type)
	{
		case AMF_NUMBER:
			data = AMF_EncodeNumber(data,data_len, prop->p_vu.p_number);
			break;

		case AMF_BOOLEAN:
			data = AMF_EncodeBoolean(data,data_len,prop->p_vu.p_number != 0);
			break;

		case AMF_STRING:
			data = AMF_EncodeString(data, data_len,&prop->p_vu.p_aval);
			break;

		case AMF_NULL:
			*data++ = AMF_NULL;
			break;

		case AMF_OBJECT:
			data = AMF_Encode(&prop->p_vu.p_object, data,data_len);
			break;

		default:
			log_fmt(LOG_ERR, "%s, invalid type. %d", __FUNCTION__, prop->p_type);
			data = NULL;
			break;
	};
	return data;
}
char * FLV::AMF_Encode(AMFObject * obj, char *data,size_t &data_len)
{
	if (4 >= data_len)
		return NULL;
	*data++ = AMF_OBJECT;
	data_len --;
	for (int i = 0; i < obj->o_num; i++)
	{
		char *res = AMFProp_Encode(&obj->o_props[i], data,data_len);
		if (res == NULL)
		{
			log_fmt(LOG_ERR, "AMF_Encode - failed to encode property in index %d",i);
			break;
		}
		else
		{
			data = res;
		}
	}
	if(data != NULL)
		return AMF_EncodeInt24(data,data_len, AMF_OBJECT_END);
	else
		return NULL;
}

/*

*AMF3ClassDefinition
*/
void FLV::AMF3CD_AddProp(AMF3ClassDef * cd, AVal * prop)
{
	if (!(cd->cd_num & 0x0f))
		cd->cd_props = (AVal *)realloc(cd->cd_props, (cd->cd_num + 16) * sizeof(AVal));
	cd->cd_props[cd->cd_num++] = *prop;
}

AVal * FLV::AMF3CD_GetProp(AMF3ClassDef * cd, int nIndex)
{
	if (nIndex >= cd->cd_num)
		return (AVal *) & AV_empty;
	return &cd->cd_props[nIndex];
}

int FLV::FindFirstMatchingProperty(AMFObject *obj, const AVal *name,  AMFObjectProperty * p)
{
	int n;
	/* this is a small object search to locate the "duration" property */
	for (n = 0; n < obj->o_num; n++)
	{
		AMFObjectProperty *prop = AMF_GetProp(obj, NULL, n);

		if (AVMATCH(&prop->p_name, name))
		{
			*p = *prop;
			return TRUE;
		}

		if (prop->p_type == AMF_OBJECT)
		{
			if (FindFirstMatchingProperty(&prop->p_vu.p_object, name, p))
				return TRUE;
		}
	}
	return FALSE;
}

/*
*init media info
*/
int FLV::init_media_info(mediaInfo * pInfo,uint8_t * data,size_t data_len)
{
	if(pInfo == NULL || data == NULL || data_len == 0)
		return -1;

	int ret = -1;
	/*first init info*/
	pInfo->media_header_len = 0;
	pInfo->audio_bts = 0;
	pInfo->audio_channel = 1;
	pInfo->audio_sample_rate = 90000;
	pInfo->audio_sample_size = 16;
	pInfo->framerate = 1000;
	pInfo->height = 240;
	pInfo->width = 320;
	pInfo->video_bts = 0;

	/*read media info*/
	uint8_t * ptr = data;
	uint32_t tag_type = 0,tag_size = 0,slen = 0;

	bool media_tag_flag = false;
	bool video_config_tag_flag = false;
	bool audio_config_tag_flag = false;

	int tag_index = 0;
	size_t tag_array[3][2];
	memset(tag_array,0,sizeof(tag_array));

	while(ptr < data + data_len)
	{
		tag_type = utils::read_8((ubyte*)ptr);
		if(ptr + FLV_FILE_TAG_HEAD_LEN >= data + data_len)
		{
			ret = 1;
			break;
		}
		tag_size = utils::read_24((ubyte*)ptr+1);
		/*check valied video tag*/
		if ((tag_type != 18 && tag_type != 8 && tag_type != 9) || tag_size == 0)
		{
			ret = 2;
			break;
		}
		/*check tag size is completed length*/
		if(ptr + FLV_FILE_TAG_HEAD_LEN + tag_size > data + data_len)
		{
			ret = 3;
			break;
		}
		/*get metainfo*/
		if(tag_type == 18)
		{
			if(!media_tag_flag)
			{
				int mediaoffset = 11;
				AMFObject metaObj;
				metaObj.o_num = 0;
				metaObj.o_props = NULL;

				char name[64];
				memset(name,'\0',sizeof(name));
				AVal mediapars;
				mediapars.av_val = name;
				mediapars.av_len = 0;

				while(mediaoffset < (int)tag_size + 11)
				{
					AMFObjectProperty prop1;
					if(AMFProp_Decode(&prop1,(const char *)(ptr+mediaoffset),tag_size-mediaoffset+11,FALSE)< 0)
						return ret;

					AVal metastring1;
					AMFProp_GetString(&prop1,&metastring1);

					memset(name,'\0',sizeof(name));
					strcpy(name,av_onMetaData);
					mediapars.av_len = strlen(name);
					if(AVMATCH(&metastring1, &mediapars))
					{
						break;
					}
					else
					{
						mediaoffset +=  (prop1.p_vu.p_aval.av_len+3);
					}
				}

				int nRes = AMF_Decode(&metaObj, (const char *)(ptr+mediaoffset), tag_size-mediaoffset+11, FALSE);
				if (nRes < 0)
				{
					/*reset*/
					AMF_Reset(&metaObj);
					continue;
				}

				/*ok get meda data*/
				AVal metastring;
				AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0), &metastring);
				memset(name,'\0',sizeof(name));
				strcpy(name,av_onMetaData);
				mediapars.av_len = strlen(name);
				if (AVMATCH(&metastring, &mediapars))
				{
					//AMF_Dump(&metaObj);
					AMFObjectProperty prop;
					/*get width*/
					memset(name,'\0',sizeof(name));
					strcpy(name,av_width);
					mediapars.av_len = strlen(name);
					if (FindFirstMatchingProperty(&metaObj, &mediapars, &prop))
					{
						pInfo->width = (uint32_t)AMFProp_GetNumber(&prop);
					}
					/*get height*/
					memset(name,'\0',sizeof(name));
					strcpy(name,av_height);
					mediapars.av_len = strlen(name);
					if (FindFirstMatchingProperty(&metaObj, &mediapars, &prop))
					{
						pInfo->height = (uint32_t)AMFProp_GetNumber(&prop);
					}
					/*get frame rate*/
					memset(name,'\0',sizeof(name));
					strcpy(name,av_framerate);
					mediapars.av_len = strlen(name);
					if (FindFirstMatchingProperty(&metaObj, &mediapars, &prop))
					{
						pInfo->framerate = (uint32_t)AMFProp_GetNumber(&prop);
					}
					/*get video bts*/
					memset(name,'\0',sizeof(name));
					strcpy(name,av_videodatarate);
					mediapars.av_len = strlen(name);
					if (FindFirstMatchingProperty(&metaObj, &mediapars, &prop))
					{
						pInfo->video_bts = (uint32_t)AMFProp_GetNumber(&prop);
					}
					/*get audio sample rate*/
					memset(name,'\0',sizeof(name));
					strcpy(name,av_audiosamplerate);
					mediapars.av_len = strlen(name);
					if (FindFirstMatchingProperty(&metaObj, &mediapars, &prop))
					{
						pInfo->audio_sample_rate = (uint32_t)AMFProp_GetNumber(&prop);
					}
					/*get audio sample size*/
					memset(name,'\0',sizeof(name));
					strcpy(name,av_audiosamplesize);
					mediapars.av_len = strlen(name);
					if (FindFirstMatchingProperty(&metaObj, &mediapars, &prop))
					{
						pInfo->audio_sample_size = (uint32_t)AMFProp_GetNumber(&prop);
					}
					/*get audio sample rate*/
					memset(name,'\0',sizeof(name));
					strcpy(name,av_stereo);
					mediapars.av_len = strlen(name);
					if (FindFirstMatchingProperty(&metaObj, &mediapars, &prop))
					{
						pInfo->audio_channel = (uint32_t)AMFProp_GetNumber(&prop);
					}
					/*get audio data rate*/
					memset(name,'\0',sizeof(name));
					strcpy(name,av_audiodatarate);
					mediapars.av_len = strlen(name);
					if (FindFirstMatchingProperty(&metaObj, &mediapars, &prop))
					{
						pInfo->audio_bts = (uint32_t)AMFProp_GetNumber(&prop);
					}
				}
				/*reset*/
				AMF_Reset(&metaObj);
				/*record offset*/
				media_tag_flag = true;
				tag_array[tag_index][0] = ptr - data;
				tag_array[tag_index][1] = tag_size + FLV_TAG_HEADER_LEN;
				tag_index ++;
			}
			else
			{
				/*add your code here*/
			}
		}
		/*get audio aac decode args*/
		if(tag_type == 8)
		{
			if(!audio_config_tag_flag)
			{
				/*aac packet*/
				if((ptr[FLV_FILE_TAG_HEAD_LEN] >> 4) == 0x0a && ptr[FLV_FILE_TAG_HEAD_LEN+1] == 0x00 && tag_size < sizeof(pInfo->audio_config))
				{
					pInfo->audio_config_len = tag_size - 2;
					//设置参数tag时间戳=0
					memset(ptr+4,0,4);
					memcpy(pInfo->audio_config,ptr + 13,pInfo->audio_config_len);
					/*record offset*/
					audio_config_tag_flag = true;
					tag_array[tag_index][0] = ptr  -  data;
					tag_array[tag_index][1] = tag_size + FLV_TAG_HEADER_LEN;
					tag_index ++;
				}
				else
				{
					/*add your code here*/
				}
			}
			else
			{
				/*add your code here*/
			}
		}
		/*get video h264 decode args*/
		if(tag_type == 9)
		{
			if(!video_config_tag_flag)
			{
				/*avc packet*/
				if((ptr[FLV_FILE_TAG_HEAD_LEN] & 0x0f) == 0x07 && ptr[FLV_FILE_TAG_HEAD_LEN+1] == 0x00)
				{
					pInfo->priva_data_len = tag_size - 5;
					pInfo->priva_data = new ubyte[pInfo->priva_data_len + 1];
					memset(pInfo->priva_data,'\0',sizeof(pInfo->priva_data));
					memcpy(pInfo->priva_data,ptr + 16,pInfo->priva_data_len);
					//设置参数tag时间戳=0
					memset(ptr+4,0,4);
					/*record offset*/
					video_config_tag_flag = true;
					tag_array[tag_index][0] = ptr  -  data;
					tag_array[tag_index][1] = tag_size + FLV_TAG_HEADER_LEN;
					tag_index ++;
				}
				else
				{
					/*add your code here*/
				}
			}
			else
			{
				/*add your code here*/
			}
		}
		ptr += FLV_TAG_HEADER_LEN + tag_size;
		slen += FLV_TAG_HEADER_LEN + tag_size;
		/*valid flv header is completed*/
		if(media_tag_flag
			&& video_config_tag_flag
			&& audio_config_tag_flag
			&&  slen <= data_len)
		{
			/*first check is ok*/
			if(tag_array[0][0] == 0
				&& tag_array[1][0] == tag_array[0][0] + tag_array[0][1]
				&& tag_array[2][0] == tag_array[1][0] + tag_array[1][1])
			{
				/*all right,do nothing*/
			}
			else /** else go next code logic*/
			{
				/****************************************
				*	+----+-----+-------+-----+-----+------+-----+
				*	  0 	M       1      A	2	   V     3
				*****************************************/
				uint8_t * temp_buf = new uint8_t[data_len];
				size_t temp_buf_offset = 0;
				size_t m_len = 0;

				memcpy(temp_buf + temp_buf_offset,data + tag_array[0][0],tag_array[0][1]);
				temp_buf_offset += tag_array[0][1];/*M*/

				memcpy(temp_buf + temp_buf_offset,data + tag_array[1][0],tag_array[1][1]);
				temp_buf_offset += tag_array[1][1];/*A*/

				memcpy(temp_buf + temp_buf_offset,data + tag_array[2][0],tag_array[2][1]);
				temp_buf_offset += tag_array[2][1];/*V*/

				/*0 space split data*/
				m_len = tag_array[0][0];
				if(m_len > 0)
				{
					memcpy(temp_buf + temp_buf_offset,data,m_len);
					temp_buf_offset += m_len;
					m_len = 0;
				}
				/*1 space split data*/
				m_len = tag_array[1][0] - (tag_array[0][0] + tag_array[0][1]);
				if(m_len >0)
				{
					memcpy(temp_buf + temp_buf_offset,data + (tag_array[0][0] + tag_array[0][1]),m_len);
					temp_buf_offset += m_len;
					m_len = 0;
				}
				/*2 space split data*/
				m_len = tag_array[2][0] - (tag_array[1][0] + tag_array[1][1]);
				if(m_len >0)
				{
					memcpy(temp_buf + temp_buf_offset,data + (tag_array[1][0] + tag_array[1][1]),m_len);
					temp_buf_offset += m_len;
					m_len = 0;
				}
				/*3 space split data*/
				m_len = data_len - (tag_array[2][0] + tag_array[2][1]);
				if(m_len >0)
				{
					memcpy(temp_buf + temp_buf_offset,data + (tag_array[2][0] + tag_array[2][1]),m_len);
					temp_buf_offset += m_len;
					m_len = 0;
				}
				/*copy buf and free buf*/
				memcpy(data,temp_buf,temp_buf_offset);
				delete []temp_buf;
				/*valid*/
				if(temp_buf_offset != data_len)
				{
					ret = 4;
					break;
				}
			}
			/*set media header len*/
			pInfo->media_header_len =  tag_array[0][1] + tag_array[1][1] + tag_array[2][1];
			if(pInfo->framerate == 1000)
				pInfo->framerate = 15;
			ret = 0;
			break;
		}
	}
	return ret;
}

/**
 * read flv tag time
 */
uint32_t FLV::read_flv_time(uint8_t * data,size_t data_len)
{
	if(data == NULL ||  data_len < 3 || data_len > 4)
		return 0;

	uint32_t tag_time = 0;
	if(data_len == 3 || (data_len == 4 && data[3] == 0))
	{
		tag_time = utils::read_24(data);
	}
	else
	{
		tag_time =  (data[3] << 24) |  (data[0] << 16) |  (data[1] << 8) | (data[2] << 0);
	}

	return tag_time;
}

